# Chapter 15

# Real-Life Examples III

CONTENTS

- Planning the Program
- The Complete Application
- How the Program Works
  - Initializing the Game
  - Placing Cards
  - Scoring the Game
  - Ending a Game

This chapter includes a single example program: a poker solitaire game written in JavaScript. This is the largest program presented in this book and illustrates the following concepts from previous chapters:

- Functions and event handlers (Chapter 3)
- Arrays, custom objects, and arrays of objects (Chapters 4 and 10)
- Forms (Chapter 6)
- Replacing inline images (Chapter 12)

In addition, it's a good demonstration of a large-scale JavaScript application. The next sections describe and present the program.

## Planning the Program

The task for this example is to create a JavaScript poker solitaire game. The game board, at the start of a game, is shown in Figure 15.1. Here is a summary of how the game is played:

Figure 15.1 : The Poker Solitaire game board at the start of a game.

- The board consists of 25 squares in a 5-by-5 grid. A "draw" card is on the lower right corner.
- The computer deals cards one at a time. Each card appears in the draw pile. You can click on one of the 25 squares in the grid to move the card to the board.
- Scoring is based on poker hands. Each row, column, and diagonal is a separate hand, and is scored when it is completed. The total score is a combination of all of these.
- The game ends when 25 cards have been dealt. You can also click on the `New Game` link to start over.

Before you move on to the program itself, let's look at a few of the challenges of planning a project of this size:

- The board consists of 25 squares. By including a title graphic, I conveniently made these the 1st through 25th images on the page-this will make them easy to access via the `document.images` array.
- I used a custom object, `Card`, to store information about the cards in the game. This object includes a number (1-13) and a suit (c, h, s, or d.)
- The deck of cards is stored in an array called `deck`; each element is a `Card` object.
- The cards currently on the board are placed in an array called `board`. Once again, these are `Card` objects.
- I created 52 images-one for each card-and a blank image to represent blank spaces on the board. These are all exactly the same size, so they can be easily swapped inline.
- The whole game board is laid out using a table. The first column includes a link to start a new game and the total score, columns 2 through 6 are the game board, and the last column and row are used for text fields. These will hold the score for each column, row, and diagonal.

Tip
You'll find the graphics you need for this example (cards, blank card, title) on the CD-ROM accompanying this book.

## The Complete Application

Without further ado, Listing 15.1 shows the complete Poker Solitaire application, including all HTML and JavaScript functions.

> Listing 15.1. (CARDS.htm) The Poker Solitaire application (HTML and JavaScript).

>     <HTML>
>     <HEAD>
>     <TITLE>Poker Solitaire</TITLE>
>     <SCRIPT LANGUAGE="JavaScript">
>     // global variables
>     var tally = new Array(14)
>     var nextcard = 1;
>     var nexti = new Image(53,68);
>     // numeric comparison for sort () function numsort (a,b){
>        return a-b;
>        }
>     function InitGame() {
>        nextcard = 1;
>     // clear scores
>        for (i=0; i<5; i++) {
>        document.form1.col[i].value = " ";
>        document.form1.row[i].value = " ";
>        document.form1.diag1.value = " ";
>        document.form1.diag2.value = " ";
>        document.form1.total.value = " ";
>        }
>     // array for board contents
>        board = new Array(26);
>        for (i=1; i<26; i++) {
>           board[i] = new Card(0,"x");
>           document.images[i].src = "blank.gif";
>        }

>     // fill the deck (in order, for now)
>        deck = new Array(53);
>        for (i=1; i<14; i++) {
>          deck[i] = new Card(i,"c");
>          deck[i+13] = new Card(i,"h");
>          deck[i+26] = new Card(i,"s");
>          deck[i+39] = new Card(i,"d");
>        }
>     // shuffle the deck
>        n = Math.floor(52 * Math.random() + 200);
>        for (i=1; i<n; i++) {
>           card1 = Math.floor(52*Math.random() + 1);
>           card2 = Math.floor(52*Math.random() + 1);
>           if (card1 != card2) {
>              temp = deck[card2];
>              deck[card2] = deck[card1];
>              deck[card1] = temp;
>           }
>        }
>     // draw the first card on screen
>        document.images[26].src = deck[nextcard].fname();
>        nexti.src = deck[nextcard+1].fname();
>     // end InitGame
>     }

>     // place the draw card on the board where clicked

>     function PlaceCard(pos) {
>        if (board[pos].suit != "x") {
>           return;
>        }
>        document.images[pos].src = document.images[26].src;
>        document.images[26].src = "blank.gif";
>        board[pos] = deck[nextcard];
>        nextcard++;
>        Score();
>        if (nextcard > 25) {
>           EndGame();
>        }
>        else {
>           document.images[26].src = deck[nextcard].fname();
>     // cache next image for draw pile
>           nexti = new Image(53,68);
>           nexti.src = deck[nextcard+1].fname();
>        }
>     }

>     // check for completed rows and display row scores
>     function Score() {
>        totscore = 0;
>     // rows
>        for (x=0; x<5; x++) {
>           r = x * 5 + 1;
>           a = AddScore(board[r],board[r+1],board[r+2],board[r+3],board[r+4])
>           if (a != -1) {
>              document.form1.row[x].value = a;
>              totscore += a;
>           }
>        }
>     // columns
>        for (x=0; x<5; x++) {
>           r = x + 1;
>           a = AddScore(board[r],board[r+5],board[r+10],board[r+15],board[r+20])
>           if (a != -1) {
>              document.form1.col[x].value = a;
>              totscore += a;
>           }
>        }
>     // diagonals
>           a = AddScore(board[5],board[9],board[13],board[17],board[21])
>           if (a != -1) {
>              document.form1.diag1.value = a;
>              totscore += a;
>           }
>           a = AddScore(board[1],board[7],board[13],board[19],board[25])
>           if (a != -1) {
>              document.form1.diag2.value = a;
>              totscore += a;
>           }
>           document.form1.total.value = totscore;
>     }

>     // check for poker hands
>     function AddScore(c1,c2,c3,c4,c5) {
>        straight = false;
>        flush = false;
>        pairs = 0;
>        three = false;

>     // sorted array for convenience
>        nums = new Array(5);
>        nums[0] = c1.num;
>        nums[1] = c2.num;
>        nums[2] = c3.num;
>        nums[3] = c4.num;
>        nums[4] = c5.num;
>        nums.sort(numsort);
>     // no score if row is not filled
>        if (c1.num == 0 || c2.num == 0 || c3.num == 0
>            || c4.num == 0 || c5.num == 0) {
>           return -1;
>        }
>     // flush
>        if (c1.suit == c2.suit && c2.suit == c3.suit
>           && c3.suit == c4.suit && c4.suit == c5.suit) {
>           flush = true;
>        }
>     // straight
>        if (nums[0] + 4 == nums[1] + 3 == nums[2] +2
>             == nums[3] + 1 == nums[4]) {
>           straight = true;
>        }
>     // royal flush, straight flush, straight, flush
>        if (straight && flush && nums[4]==13) return 250;
>        if (straight && flush) return 50;
>        if (straight) return 4;
>        if (flush) return 5;

>     // tally array is a count for each card value
>        for (i=1; i<14; i++) {
>           tally[i] = 0;
>        }
>        for (i=0; i<5; i++) {
>           tally[nums[i]] += 1;
>        }

>        for (i=1; i<14; i++) {
>     // four of a kind
>           if (tally[i] == 4) return 25;
>           if (tally[i] == 3) three = true;
>           if (tally[i] == 2) pairs += 1;
>        }
>     // full house
>        if (three && pairs == 1) return 8;
>     // two pair
>        if (pairs == 2) return 2;
>     // three of a kind
>        if (three) return 3;
>     // just a pair
>        if (pairs == 1) return 1;
>     // nothing
>        return 0;
>     // end AddScore()
>     }

>     // game over - final score
>     function EndGame() {
>        document.images[26].src = "blank.gif";
>        window.alert("Game Over");
>     }

>     // make a filename for an image, given Card object
>     function fname() {
>        return this.num + this.suit + ".gif";
>     }

>     // constructor for Card objects
>     function Card(num,suit) {
>        this.num = num;
>        this.suit = suit;
>        this.fname = fname;
>     }

>     </SCRIPT>
>     </HEAD>
>     <BODY>
>     <FORM NAME="form1">
>     <TABLE>
>     <tr>
>       <td> <img src="title.gif" height=59 width=150> </td>
>       <td> <a href="#" onClick="PlaceCard(1);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(2);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(3);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(4);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(5);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="row"> </td>
>       <td> </td>
>     </tr>
>     <tr>
>       <td> </td>
>       <td> <a href="#" onClick="PlaceCard(6);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(7);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(8);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(9);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(10);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="row"> </td>
>       <td> </td>
>     </tr>
>     <tr>
>       <td> <B>Total<BR>Score:</B>
>            <INPUT TYPE="TEXT" SIZE=5 NAME="total"></td>
>       <td> <a href="#" onClick="PlaceCard(11);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(12);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(13);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(14);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(15);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="row"> </td>
>       <td> </td>
>     </tr>
>     <tr>
>       <td> <a href="#" onClick="InitGame();">
>            <b>New Game</b></a>
>            <br><b><a href="psoldoc.html">Instructions</b></a></td>
>       <td> <a href="#" onClick="PlaceCard(16);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(17);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(18);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(19);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(20);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="row"> </td>
>       <td> <b><BR>Next<BR>Card:</b></td>
>     </tr>
>     <tr>
>       <td> </td>
>       <td> <a href="#" onClick="PlaceCard(21);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(22);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(23);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(24);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <a href="#" onClick="PlaceCard(25);">
>            <img border=0 src="blank.gif" height=68 width=53></a>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="row"> </td>
>       <td> <img src="blank.gif" height=68 width=53></td>
>     </tr>
>     <tr>
>       <td align=right> <INPUT TYPE="TEXT" SIZE=4 NAME="diag1"> </td>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="col"> </td>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="col"> </td>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="col"> </td>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="col"> </td>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="col"> </td>
>       <td> <INPUT TYPE="TEXT" SIZE=4 NAME="diag2"> </td>
>     </TABLE>
>     </FORM>

>     </BODY>
>     </HTML>

## How the Program Works

The HTML for this application is nearly as complicated as the JavaScript code, but it's easy because it's repetitive. Each of the images in the game board includes an `onClick` event handler that calls the `PlaceCard()` function, described later.

In the next few sections, you'll take a look at how the program works.

### Initializing the Game

The `InitGame()` function, shown in Listing 15.2, is called when the page loads and also when you use the `New Game` link. It performs the following tasks:

- Stores 52 `Card` objects, in order, in the `deck` array
- Initializes the `board` array with null values
- Shuffles the deck by swapping a random number of random cards
- Places the first card on the draw pile (otherwise known as `document.images[26]`)

> Listing 15.2. The `InitGame()` function begins a new game.

>     function InitGame() {
>        nextcard = 1;
>     // clear scores
>        for (i=0; i<5; i++) {
>        document.form1.col[i].value = " ";
>        document.form1.row[i].value = " ";
>        document.form1.diag1.value = " ";
>        document.form1.diag2.value = " ";
>        document.form1.total.value = " ";
>        }
>     // array for board contents
>        board = new Array(26);
>        for (i=1; i<26; i++) {
>           board[i] = new Card(0,"x");
>           document.images[i].src = "blank.gif";
>        }

>     // fill the deck (in order, for now)
>        deck = new Array(53);
>        for (i=1; i<14; i++) {
>          deck[i] = new Card(i,"c");
>          deck[i+13] = new Card(i,"h");
>          deck[i+26] = new Card(i,"s");
>          deck[i+39] = new Card(i,"d");
>        }
>     // shuffle the deck
>        n = Math.floor(52 * Math.random() + 200);
>        for (i=1; i<n; i++) {
>           card1 = Math.floor(52*Math.random() + 1);
>           card2 = Math.floor(52*Math.random() + 1);
>           if (card1 != card2) {
>              temp = deck[card2];
>              deck[card2] = deck[card1];
>              deck[card1] = temp;
>           }
>        }
>     // draw the first card on screen
>        document.images[26].src = deck[nextcard].fname();
>        nexti.src = deck[nextcard+1].fname();
>     // end InitGame
>     }

### Placing Cards

Once the `InitGame()` function is finished, the program isn't running at all-that's the beauty of event-based programming. The next step is up to the user, who should click on one of the squares of the game board.

When a square is clicked, the `PlaceCard()` function, shown in Listing 15.3, is called. This function moves the card from the draw pile to the appropriate square. Because it's already in the cache, this happens instantly. An `image` object is then created to preload the next card. Figure 15.2 shows a game in progress with several cards placed.

Figure 15.2 : The Poker Solitaire game in progress.

> Listing 15.3. The `PlaceCard()` function places the next card.

>     function PlaceCard(pos) {
>        if (board[pos].suit != "x") {
>           return;
>        }
>        document.images[pos].src = document.images[26].src;
>        document.images[26].src = "blank.gif";
>        board[pos] = deck[nextcard];
>        nextcard++;
>        Score();
>        if (nextcard > 25) {
>           EndGame();
>        }
>        else {
>           document.images[26].src = deck[nextcard].fname();
>     // cache next image for draw pile
>           nexti = new Image(53,68);
>           nexti.src = deck[nextcard+1].fname();
>        }
>     }

### Scoring the Game

The `Score()` function, shown in Listing 15.4, is called each time a card is placed. This routine scans through the five rows and five columns and the two diagonals. The cards for each row are passed to the `AddScore()` function, which does the tricky part-finding poker hands and scoring them.

> Listing 15.4. The `Score()` function scores the rows and columns.

>     function Score() {
>        totscore = 0;
>     // rows
>        for (x=0; x<5; x++) {
>           r = x * 5 + 1;
>           a = AddScore(board[r],board[r+1],board[r+2],board[r+3],board[r+4])
>           if (a != -1) {
>              document.form1.row[x].value = a;
>              totscore += a;
>           }
>        }
>     // columns
>        for (x=0; x<5; x++) {
>           r = x + 1;
>           a = AddScore(board[r],board[r+5],board[r+10],board[r+15],board[r+20])
>           if (a != -1) {
>              document.form1.col[x].value = a;
>              totscore += a;
>           }
>        }
>     // diagonals
>           a = AddScore(board[5],board[9],board[13],board[17],board[21])
>           if (a != -1) {
>              document.form1.diag1.value = a;
>              totscore += a;
>           }
>           a = AddScore(board[1],board[7],board[13],board[19],board[25])
>           if (a != -1) {
>              document.form1.diag2.value = a;
>              totscore += a;
>           }
>           document.form1.total.value = totscore;
>     }

The following scores are assigned to the various poker hands, based on their relative probability:

Poker hand | Score
Pair | 1
2 pair | 2
3 of a kind | 3
Straight | 4
Flush | 5
Full house | 8
4 of a kind | 25
Straight flush | 50
Royal Flush | 250

### Ending a Game

The `EndGame()` function, shown in Listing 15.5, is called when 25 cards have been played. This function is simple; it "blanks out" the draw card and informs you that the game is over with an alert. Because the total score is updated continuously, there's no need to worry about it here.

> Listing 15.5. The `EndGame()` function ends the game.

>     // game over - final score
>     function EndGame() {
>        document.images[26].src = "blank.gif";
>        window.alert("Game Over");
>     }

Figure 15.3 shows the screen after a reasonably successful game. That's it! I hope this example shows you how much you can do with JavaScript and inspires you to even bigger things.

Figure 15.3 : The Poker Solitaire game display after all cards have been played.